package io.renren.wechat.service;

import com.alibaba.fastjson.annotation.JSONField;
import io.renren.wechat.common.WechatResponse;
import io.renren.wechat.config.WechatProperties;
import io.renren.wechat.service.suite.SuiteService;
import io.renren.wechat.service.token.AccessTokenService;
import io.renren.wechat.support.ResponseEntityUtils;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.TimeUnit;

/**
 * @author dmcp@qq.com
 * @since 2021/12/1 23:31
 */
@Component
public class JSAPITicketService {
    private static final Logger LOGGER = LoggerFactory.getLogger(JSAPITicketService.class);
    private SuiteService suiteService;
    private WechatProperties wechatProperties;
    private AccessTokenService accessTokenService;
    private RestTemplate fastRestTemplate;
    private StringRedisTemplate stringRedisTemplate;

    private static final String SELF_CORP_TICKET_URL = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=%s";
    private static final String AGENT_TICKET_URL = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=%s&type=agent_config";

    /**
     * 获取企业的jsapi_ticket
     *
     * @return 企业的jsapi_ticket
     */
    public String getCorpTicket() {
        final String corpId = wechatProperties.getCorpId();

        return getCorpTicketFromCache(corpId);
    }

    /**
     * 获取应用的jsapi_ticket
     *
     * @param corpId 企业id
     * @return 应用的jsapi_ticket
     */
    public String getAgentTicket(String corpId) {
        if (StringUtils.isBlank(corpId)) {
            throw new RuntimeException("corpId不能为空");
        }

        return getAgentTicketFromCache(corpId);
    }

    private String getAgentTicketFromCache(String corpId) {
        final String ticketCacheKey = buildAgentTicketCacheKey(corpId);
        LOGGER.debug("getting agent ticket for key: [{}]", ticketCacheKey);
        String ticket = stringRedisTemplate.opsForValue().get(ticketCacheKey);

        if (StringUtils.isNotBlank(ticket)) {
            LOGGER.debug("got agent ticket for key: [{}] ticket: [{}] in cache", ticketCacheKey, ticket);
            return ticket;
        }

        final TicketResponse ticketResponse = doGetAgentTicket(corpId);
        ticket = ticketResponse.getTicket();

        stringRedisTemplate.opsForValue().set(ticketCacheKey, ticket, ticketResponse.getExpiresIn(), TimeUnit.SECONDS);

        return ticket;
    }

    /**
     * 获取应用的jsapi_ticket
     *
     * @param corpId 3td corpId
     * @return 应用的jsapi_ticket
     */
    protected String getCorpTicketFromCache(String corpId) {
        final String ticketCacheKey = buildCorpTicketCacheKey(corpId);
        LOGGER.debug("getting corp ticket for key: [{}]", ticketCacheKey);
        String ticket = stringRedisTemplate.opsForValue().get(ticketCacheKey);

        if (StringUtils.isNotBlank(ticket)) {
            LOGGER.debug("got corp ticket for key: [{}] ticket: [{}] in cache", ticketCacheKey, ticket);
            return ticket;
        }

        final TicketResponse ticketResponse = doGetCorpTicket(corpId);
        ticket = ticketResponse.getTicket();

        stringRedisTemplate.opsForValue().set(ticketCacheKey, ticket, ticketResponse.getExpiresIn(), TimeUnit.SECONDS);

        return ticket;
    }

    private TicketResponse doGetAgentTicket(String corpId) {
        String accessToken = suiteService.getAuthCorpAccessToken(corpId, 0);

        return doGetTicketFromWX(accessToken, true);
    }

    private TicketResponse doGetCorpTicket(String corpId) {
        String accessToken = accessTokenService.getSelfCorpAccessToken();

        return doGetTicketFromWX(accessToken, false);
    }

    private TicketResponse doGetTicketFromWX(String accessToken, boolean isGetAgentTicket) {
        String requestUrl;
        if (isGetAgentTicket) {
            requestUrl = String.format(AGENT_TICKET_URL, accessToken);
        } else {
            requestUrl = String.format(SELF_CORP_TICKET_URL, accessToken);
        }
        LOGGER.debug("getting ticket from wx with url: {}", requestUrl);

        final ResponseEntity<TicketResponse> responseEntity = fastRestTemplate.getForEntity(requestUrl, TicketResponse.class);

        LOGGER.debug("doGetTicketFromWX response entity: {}", responseEntity);

        if (!responseEntity.getStatusCode().is2xxSuccessful()) {
            throw new RuntimeException("调用[getTicket]请求执行失败");
        }

        final TicketResponse response = responseEntity.getBody();
        if (response == null) {
            LOGGER.warn("obtain getTicket failed, response body is empty!");
            throw new RuntimeException("获取 getTicket 失败，响应为空");
        }

        boolean isError = ResponseEntityUtils.isError(responseEntity) || response.isError();
        if (isError) {
            LOGGER.warn(">>>>>>>obtain getTicket failed, errMsg: {}", response.getErrmsg());
            throw new RuntimeException("获取 getTicket 失败");
        }

        return response;
    }

    private String buildAgentTicketCacheKey(String corpId) {
        return String.format("Agent:Ticket:%s", corpId);
    }

    private String buildCorpTicketCacheKey(String corpId) {
        return String.format("Corp:Ticket:%s", corpId);
    }

    @EqualsAndHashCode(callSuper = true)
    @Data
    static class TicketResponse extends WechatResponse {
        private String ticket;

        @JSONField(name = "expires_in")
        private Integer expiresIn;
    }

    @Autowired
    public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Autowired
    public void setFastRestTemplate(RestTemplate fastRestTemplate) {
        this.fastRestTemplate = fastRestTemplate;
    }

    @Autowired
    public void setWechatProperties(WechatProperties wechatProperties) {
        this.wechatProperties = wechatProperties;
    }

    @Autowired
    public void setSuiteService(SuiteService suiteService) {
        this.suiteService = suiteService;
    }

    @Autowired
    public void setAccessTokenService(AccessTokenService accessTokenService) {
        this.accessTokenService = accessTokenService;
    }
}
